package cate.game.role.mall;

import cate.common.table.activity.base.ShopActivityBaseRow;
import cate.common.table.d.GDMall;
import cate.common.table.mall.row.ILimitBuyBaseRow;
import cate.common.table.pay.PayItemRow;
import cate.common.util.GameResult;
import cate.game.res.MixRes;
import cate.game.res.MixResAdder;
import cate.game.res.MixResItem;
import cate.game.role.Role;
import cate.game.role.RoleDependent;
import cate.game.role.pay.IPayable;
import com.fasterxml.jackson.annotation.JsonIgnore;
import easy.java.dev.note.NoteField;
import easy.java.practice.errorcode.EcResult;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Predicate;

public abstract class AbstractLimitBuyMall extends RoleDependent implements IPayable {
    @NoteField(value = "限购数量记录")
    public Map<Integer, Integer> buyRecords;

    @Override
    public void initialize(Role role) {
        super.initialize(role);
        if (buyRecords == null) {
            buyRecords = new HashMap<>();
        }
    }

    @Override
    public void onEnter() {
        excelCheck();
    }

    private Predicate<? super Integer> createResetConsume(GDMall.ResetType resetType){
        return k -> {
            ILimitBuyBaseRow config = getConfig(k);
            return config != null && config.getResetType() == resetType.getType();
        };
    }

    protected boolean resetRecords(GDMall.ResetType resetType){
        AtomicBoolean anyMatch = new AtomicBoolean(false);
        Predicate<? super Integer> action = createResetConsume(resetType);
        buyRecords.entrySet().forEach(e -> {
            if(action.test(e.getKey())){
                e.setValue(0);
                anyMatch.set(true);
            }
        });
        return anyMatch.get();
    }

    protected abstract void noticeUpdate();

    @Override
    public void onDaySpan(boolean silence) {
        resetRecords(GDMall.ResetType.DAILTY);
        if (!silence) {
            noticeUpdate();
        }
    }

    @Override
    public void onWeekSpan(boolean silence){
        resetRecords(GDMall.ResetType.WEEKLY);
        if (!silence) {
            noticeUpdate();
        }
    }

    @Override
    public void onMonthSpan(boolean silence){
        resetRecords(GDMall.ResetType.MONTHLY);
        if (!silence) {
            noticeUpdate();
        }
    }

    @Override
    public GameResult<Void> allowPay(PayItemRow pir) {
        GameResult<Void> r = new GameResult<>();
        int tid = pir.funcSubId;
        if (!buyRecords.containsKey(tid)) {
            return r.fail("未找到配置1");
        }
        ILimitBuyBaseRow config = getConfig(tid);
        if (config == null) {
            return r.fail("未找到配置2");
        }
        if (config.getOpenDay() > role.getGame().getOpenDay()) {
            return r.fail("开服天数不足");
        }
        if (config.getLimit() != -1) {
            if (buyRecords.get(tid) + 1 > config.getLimit()) {
                return r.fail("限购次数不足");
            }
        }
        if (config.getBuyType() != GDMall.MallType.MALL_TYPE_PAY.getType()) {
            return r.fail("不支持此购买模式");
        }
        MixRes rewardRes = new MixRes(config.getRewardStr());
        if (rewardRes.isEmpty()) {
            return r.fail("奖励为空");
        }
        return r.success();
    }

    /**
     * 获取操作类型
     */
    @JsonIgnore
    protected abstract int getGDOperation();

    /**
     * 获取单条商品配置，会重复遍历，建议override
     *
     * @param tid 配置ID
     */
    protected ILimitBuyBaseRow getConfig(int tid) {
        return getConfigs().get(tid);
    }

    @Override
    public void onPaySuccess(PayItemRow payItemRow) {
        provideReward(payItemRow.funcSubId, 1, true);
        noticeUpdate();
    }

    /**
     * 获取活动内所有商品配置
     */
    @JsonIgnore
    protected abstract Map<Integer, ILimitBuyBaseRow> getConfigs();

    protected void excelCheck() {
        Map<Integer, ILimitBuyBaseRow> allConfigs = getConfigs();
        if (allConfigs.isEmpty()) {
            buyRecords.clear();
            return;
        }
        buyRecords.entrySet().removeIf(kv -> !allConfigs.containsKey(kv.getKey()));
        for (ILimitBuyBaseRow config : allConfigs.values()) {
            if (!buyRecords.containsKey(config.getId())) {
                buyRecords.put(config.getId(), 0);
            }
        }
    }

    /**
     * 购买商品
     *
     * @param tid 配置ID
     * @param num 数量
     * @return 购买结果 携带自身活动数据
     */
    public GameResult<Void> buyItem(int tid, int num) {
        GameResult<Void> r = new GameResult<>();
        if (!buyRecords.containsKey(tid)) {
            return r.fail("未找到配置1");
        }
        ILimitBuyBaseRow config = getConfig(tid);
        if (config == null) {
            return r.fail("未找到配置2");
        }
        if (config.getOpenDay() > role.getGame().getOpenDay()) {
            return r.fail("开服天数不足");
        }
        if (config.getLimit() != -1 && buyRecords.get(tid) > config.getLimit() - num) {
            return r.fail("限购次数不足");
        }
        if (config.getBuyType() == GDMall.MallType.MALL_TYPE_PAY.getType()) {
            return r.fail("不支持此购买模式");
        }
        MixRes rewardRes = new MixRes(config.getRewardStr());
        if (rewardRes.isEmpty()) {
            return r.fail("奖励为空");
        }
        MixRes costRes = new MixRes(config.getCostStr());
        if (!costRes.isEmpty()) {
            costRes.items.forEach(item -> item.num *= num);
            EcResult<MixResItem> consumeRes = costRes.consumeCheck(role, null);
            if (!consumeRes.ok()) {
                return r.fail(consumeRes.message);
            }
            role.log.setOperation(getGDOperation(), tid);
            costRes.consume(role);
            role.log.clearOperation();
        }
        provideReward(tid, num, true);
        return r.success();
    }

    /**
     * 发放物品
     *
     * @param tid 配置ID
     * @param num 数量
     */
    protected MixRes provideReward(int tid, int num, boolean dialog) {
        if (!buyRecords.containsKey(tid)) {
            return null;
        }
        ILimitBuyBaseRow config = getConfig(tid);
        if (config == null) {
            return null;
        }
        MixRes rewardRes = new MixRes(config.getRewardStr());
        if (rewardRes.isEmpty()) {
            return null;
        }
        buyRecords.put(tid, buyRecords.get(tid) + num);
        rewardRes.items.forEach(item -> item.num *= num);
        new MixResAdder().setRes(rewardRes)
                .setOperation(getGDOperation())
                .setOperationSub(tid)
                .setDialogReward(dialog)
                .setSendMailWhenFull(true)
                .exe(role);
        return rewardRes;
    }
}
